/*
 Rainbowduino_NooTe v3.0 Library. 
 
 2011 Copyright (c) Seeed Technology Inc.
 
 Authors: Albert.Miao, Visweswara R

 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
 License as published by the Free Software Foundation; either
 version 2.1 of the License, or (at your option) any later version.
 
 This library is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

*/

/*
This library heavily makes use of line, circle and other shape drawing algorithms and code presented
in paper "The Beauty of Bresenham's Algorithm" by Alois Zingl, Vienna, Austria. The original author
has kindly released these example programs with no copyright.
*/

#include "Rainbowduino_NooTe.h"

Rainbowduino_NooTe Rb;

extern unsigned char simpleFont[][8];

//Frame Buffer -- placed in RAM
unsigned char frameBuffer[3][8][8]=
{

    {//blue
        {0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0}
    },

    {//green
        {0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0}
    },

        {//red
        {0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0}
    }
};

unsigned char lineDrive=0; //used within ISR

//Init the Port lines and invoke timer 1 configuration

void Rainbowduino_NooTe::init()
{
    DDR_Lines |= BIT_Lines;
    PORT_Lines &=~ BIT_Lines;

    DDRD |= 0x04;

    DDR_Data |= BIT_Data;
    DDR_Clk |= BIT_Clk;
    PORT_Data &=~ BIT_Data;
    PORT_Clk &=~ BIT_Clk;

    DDRB |= 0x20;

    clearDisplay();
    init_timer1();  //Configure interrupt
}

//Configure Timer 1 ISR for periodic 100uS interrupts

void Rainbowduino_NooTe::init_timer1(void)		//initialize Timer1 to overflow every  100uS
{
    TCCR1A = 0;                 		// clear control register A
    TCCR1B = _BV(WGM13);        		// set mode as phase and frequency correct pwm, stop the timer
    ICR1 = 10000;               		//(XTAL * microseconds) / 2000000  1mS cycles
    TIMSK1 = _BV(TOIE1);
    TCNT1 = 0;
    TCCR1B |= _BV(CS10);
    sei();                      		//enable global interrupt
}

//Routine to send 16bit data to MY9221 driver chips

void Rainbowduino_NooTe::send16bitData(uint32_t data)
{
    for(unsigned char i=0;i<16;i++)
    {
        if(data&0x8000)
        {
            PORT_Data |= BIT_Data;
        }
        else
        {
            PORT_Data &=~ BIT_Data;
        }

        PORT_Clk ^= BIT_Clk;
        data <<= 1;
    }
}

//latch routine for MY9221 data exchange

void Rainbowduino_NooTe::latchData(void)
{
    PORT_Data &=~ BIT_Data;

    delayMicroseconds(10);
    switchOffDrive;
    for(unsigned char i=0;i<8;i++)
    {
        PORT_Data ^= BIT_Data;
    }
} 

void Rainbowduino_NooTe::switchOnDrive(uint8_t line)
{
  unsigned char LineBits = ((line)<<4);
  PORT_Lines &=~ BIT_Lines;
  PORT_Lines |= LineBits;
  PORT_Lines |= 0x80;
}

//clear MY9221 lines. Internally used for avoiding flicker. This is not the same as blank disply.

void Rainbowduino_NooTe::clearDisplay(void)
{
    unsigned char i=0;
    unsigned char f=0;

    send16bitData(CmdMode);
    PORT_Data &=~ BIT_Data;
    for(i=0;i<192;i++)
    {
        {
            PORT_Clk ^= BIT_Clk;
        }
    }

    send16bitData(CmdMode);
    PORT_Data &=~ BIT_Data;
    for(i=0;i<192;i++)
    {
        {
            PORT_Clk ^= BIT_Clk;
        }
    }
    latchData();

}

//blank all pixels
void Rainbowduino_NooTe::blankDisplay(void)
{
   for(unsigned char x=0;x<=7;x++)
     {
      for(unsigned char y=0;y<=7;y++)
       {
        frameBuffer[0][x][y] = 0x00;
        frameBuffer[1][x][y] = 0x00;
        frameBuffer[2][x][y] = 0x00;
       }
     }
}

uint32_t Rainbowduino_NooTe::makeColor(uint8_t red, uint8_t green, uint8_t blue)
{
  return (((uint32_t)red)<<16) | (((uint32_t)green)<<8) | blue;
}

//set the pixel (X,Y) of RGB matrix with colour 24-bit RGB Colour 
void Rainbowduino_NooTe::drawPixel(int16_t x, int16_t y, uint32_t colorRGB)
{
    if(x >= 0 && y >= 0 && x <= 7 && y <= 7)
    {
		colorRGB = (colorRGB & 0x00FFFFFF);
		frameBuffer[0][x][y]=(colorRGB & 0x0000FF); //channel Blue
		colorRGB = (colorRGB >> 8);
		frameBuffer[1][x][y]=(colorRGB & 0x0000FF); //channel Green
		colorRGB = (colorRGB >> 8);
		frameBuffer[2][x][y]=(colorRGB & 0x0000FF); //channel Red
    }
}

void Rainbowduino_NooTe::drawCircle(int16_t poX, int16_t poY, uint16_t r, uint32_t color)
{
    int x = -r, y = 0, err = 2-2*r, e2;
    do {
        drawPixel(poX-x, poY+y,color);
        drawPixel(poX+x, poY+y,color);
        drawPixel(poX+x, poY-y,color);
        drawPixel(poX-x, poY-y,color);
		
        e2 = err;
        if (e2 <= y) {
            err += ++y*2+1;
            if (-x == y && e2 <= x) e2 = 0;
        }
        if (e2 > x) err += ++x*2+1;
    }
    while (x <= 0);
}

void Rainbowduino_NooTe::fillCircle(int16_t poX, int16_t poY, uint16_t r, uint32_t color)
{
    int x = -r, y = 0, err = 2-2*r, e2;
    do {
        drawVerticalLine(poX-x,poY-y,2*y,color);
        drawVerticalLine(poX+x,poY-y,2*y,color);

        e2 = err;
        if (e2 <= y) {
            err += ++y*2+1;
            if (-x == y && e2 <= x) e2 = 0;
        }
        if (e2 > x) err += ++x*2+1;
    }
    while (x <= 0);

}

void Rainbowduino_NooTe::drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint32_t color)
{
    int dx = abs(x1-x0), sx = x0<x1 ? 1 : -1;
    int dy = -abs(y1-y0), sy = y0<y1 ? 1 : -1;
    int err = dx+dy, e2; /* error value e_xy */
    for (;;){ /* loop */
        drawPixel(x0,y0,color);
        e2 = 2*err;
        if (e2 >= dy) { /* e_xy+e_x > 0 */
            if (x0 == x1) break;
            err += dy;
            x0 += sx;
        }
        if (e2 <= dx) { /* e_xy+e_y < 0 */
            if (y0 == y1) break;
            err += dx;
            y0 += sy;
        }
    }
}

void Rainbowduino_NooTe::drawVerticalLine(int16_t poX, int16_t poY, uint16_t length, uint32_t color)
{
	for(int s=0; s<length; s++)
	{
		drawPixel(poX, poY+s, color);
	}
}

void Rainbowduino_NooTe::drawHorizontalLine(int16_t poX, int16_t poY, uint16_t length, uint32_t color)
{
 	for(int s=0; s<length; s++)
	{
		drawPixel(poX+s, poY, color);
	}
}

void Rainbowduino_NooTe::drawRectangle(int16_t poX, int16_t poY, uint16_t length, uint16_t width, uint32_t color)
{
    drawHorizontalLine(poX, poY, length, color);
    drawHorizontalLine(poX, poY+width-1, length, color);
    drawVerticalLine(poX, poY, width,color);
    drawVerticalLine(poX+length-1, poY, width,color);
}

void Rainbowduino_NooTe::fillRectangle(int16_t poX, int16_t poY, uint16_t length, uint16_t width, uint32_t color)
{
    for(unsigned int i=0;i<width;i++)
    {
        drawHorizontalLine(poX, poY+i, length, color);
    }
}

void Rainbowduino_NooTe::drawSquare(int16_t poX, int16_t poY, uint16_t length, uint32_t color)
{
   drawRectangle(poX, poY, length, length, color);
}

void Rainbowduino_NooTe::fillSquare(int16_t poX, int16_t poY, uint16_t length, uint32_t color)
{
   fillRectangle(poX, poY, length, length, color);
}

void Rainbowduino_NooTe::fill(uint32_t color)
{
   fillRectangle(0, 0, 8, 8, color);
}

void Rainbowduino_NooTe::drawChar(int16_t poX, int16_t poY, uint8_t ascii, uint32_t colorRGB)
{
    if((ascii < 0x20)||(ascii > 0x7e))//Unsupported char.
    {
        ascii = '?';
    }
    for(unsigned char i=0;i<8;i++)
    {
        unsigned char temp = pgm_read_byte(&simpleFont[ascii-0x20][i]);
        for(unsigned char f=0;f<8;f++)
        {
            if((temp>>f)&0x01)
            {
                drawPixel(poY+f, poX+i, colorRGB);
            }
        }
    }
}


//Timer1 Interrupt Service Routine
//All frameBuffer data exchange happens here

ISR(TIMER1_OVF_vect)          
{
    //An Ideal ISR has to be short and not make any function calls
    //But, in this case only data exchange happens.

    Rb.clearDisplay(); //clear current display to avoid the next line dim light

    unsigned char line = (lineDrive & 0x07);

    Rb.send16bitData(CmdMode);

    Rb.send16bitData(frameBuffer[0][line][7]);
    Rb.send16bitData(frameBuffer[0][line][6]);
    Rb.send16bitData(frameBuffer[0][line][5]);
    Rb.send16bitData(frameBuffer[0][line][4]);
    Rb.send16bitData(frameBuffer[0][line][3]);
    Rb.send16bitData(frameBuffer[0][line][2]);
    Rb.send16bitData(frameBuffer[0][line][1]);
    Rb.send16bitData(frameBuffer[0][line][0]);

    Rb.send16bitData(frameBuffer[1][line][7]);
    Rb.send16bitData(frameBuffer[1][line][6]);
    Rb.send16bitData(frameBuffer[1][line][5]);
    Rb.send16bitData(frameBuffer[1][line][4]);

    Rb.send16bitData(CmdMode);

    Rb.send16bitData(frameBuffer[1][line][3]);
    Rb.send16bitData(frameBuffer[1][line][2]);
    Rb.send16bitData(frameBuffer[1][line][1]);
    Rb.send16bitData(frameBuffer[1][line][0]);

    Rb.send16bitData(frameBuffer[2][line][7]);
    Rb.send16bitData(frameBuffer[2][line][6]);
    Rb.send16bitData(frameBuffer[2][line][5]);
    Rb.send16bitData(frameBuffer[2][line][4]);
    Rb.send16bitData(frameBuffer[2][line][3]);
    Rb.send16bitData(frameBuffer[2][line][2]);
    Rb.send16bitData(frameBuffer[2][line][1]);
    Rb.send16bitData(frameBuffer[2][line][0]);

    Rb.latchData();
    Rb.switchOnDrive(line);
    lineDrive++;

    PORTD &=~ 0x04;
}






